package com.parking.order.state;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.parking.common.core.model.RedisKey;
import com.parking.common.core.model.StateData;
import com.parking.common.web.util.RedisUtil;
import com.parking.order.client.dto.UserInfoDto;
import com.parking.order.dao.EntranceInfoDao;
import com.parking.order.dao.OrderInfoDao;
import com.parking.order.model.EntranceInfo;
import com.parking.order.model.OrderInfo;
import com.parking.order.model.exception.OrderException;
import com.parking.order.model.exception.OrderExceptionCode;
import com.parking.order.util.BasicInfoUtil;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * 订单状态 0待支付 1已支付 2可开始 3已开始 4需结束 5已结束 6已取消
 *         NOPAY  PAID   SHBEGIN BEGIN SHEND  END    CANCEL
 * 订单状态变化类汇总了所有存在的订单状态变化对(state1,state2)
 * 同时以注释的形式指出在什么情况下会出现这些状态变化
 *
 * 属于订单接口的工具类
 *
 * 支付行为相关的状态变化
 * (0,1) 支付订单且支付时间在开始时间之前
 * (0,2) 支付订单且支付时间在开始时间之后
 * (0,6) 未支付状态取消订单 或 固定时间内不支付自动取消订单
 * (1,2) 扫描到开始时间为现在的(1)订单
 * (1,6) 支付状态取消订单 距离开始时间半小时以上退全款 否则退半款
 *
 * 服务行为相关的状态变化
 * (2,5) 扫描到结束时间为现在的(2)订单
 * (2,3) 记录到(2)订单入场
 * (3,4) 扫描到结束时间为现在的(3)订单 且 最新的入场纪录没有结束时间
 * (3,5) 扫描到结束时间为现在的(3)订单 且 最新的入场记录有结束时间
 * (4,5) 记录到(4)订单出场
 * */
@Component
public class CommonOrderStateChange {
    /**
     * 订单信息数据层
     * */
    @Autowired
    private OrderInfoDao orderInfoDao;

    /**
     * RabbitMQ消息发送
     * */
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 服务调用公用工具类
     * */
    @Autowired
    private BasicInfoUtil basicInfoUtil;

    /**
     * 进出场信息数据层
     * */
    @Autowired
    private EntranceInfoDao entranceInfoDao;

    /**
     * Redis工具类
     * */
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 判断订单是否存在以及状态是否正确的公共方法
     * 参数: 订单编号    orderId
     *      订单状态    orderState
     * 返回: 订单对象    OrderInfo
     *       订单Id     orderInfoId
     *       用户信息Id  userInfoId
     *       物业信息Id  certifiedInfoId
     *       物业名称    certifiedInfoName
     *       物业电话    certifiedInfoTel
     *       物业地址    certifiedInfoAddress
     *       开始时间    startTime
     *       结束时间    endTime
     *       持续小时    enduHour
     *       订单价格    orderPrice
     *       订单状态    orderInfoState
     *       订单超时状态 isOvertime
     *       订单评论状态 isComment
     *       下单时间    orderAddTime
     *       支付时间    orderPayTime
     *       最新修改时间 orderUpdateTime
     *       订单编号    orderInfoNum
     *       车位类型    stallType
     *       车位信息Id  stallInfoId
     *       车位号      stallInfo = stallInfoArea(车位区域) + stallInfoNum(车位编号)
     *       车牌号      userCarPlate
     *       用户车辆Id  userCarId
     * */
    public OrderInfo firstCheck(String orderId, Integer orderState){
        OrderInfo order = orderInfoDao.selectOne(new QueryWrapper<OrderInfo>().eq("order_info_num", orderId));
        if(order==null) throw new OrderException(OrderExceptionCode.ORDER_EXCEPTION_CODE_ORDER_NOT_EXISTS);
        if(!Objects.equals(order.getOrderInfoState(), orderState)) throw new OrderException(OrderExceptionCode.ORDER_EXCEPTION_CODE_WRONG_ORDER_STATE);
        return order;
    }


    /**
     * (0,1) 支付订单且支付时间在开始时间之前
     * 涉及接口: 支付宝主动回调接口 消息队列查询支付状态接口 用户主动退单接口
     * 条件: 状态正确 且 支付时间在开始时间之前
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeNopayToPaid(String orderId, Integer type, LocalDateTime payTime) throws Exception {
        boolean type1 = Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_SHBEGIN_RABBITMQ);

        OrderInfo order = firstCheck(orderId, StateData.ORDER_STATE_NO_PAID);
        order.setOrderInfoState(StateData.ORDER_STATE_IS_PAID);
        order.setOrderPayTime(payTime);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);

        if(type1){
            System.out.println("===================1 修改订单状态========================");
            System.out.println("订单状态:"+order.getOrderInfoState()+"(0待支付 1已支付 2可开始 3已开始 4需结束 5已结束 6已取消)");
            System.out.println("订单修改时间:"+order.getOrderUpdateTime());
            System.out.println("订单支付时间:"+order.getOrderPayTime());
        }



        // 生成订单日志
        String orderLogsDescribe = null;
        Integer logType = null;
        String logger = null;
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_PAID_ALIPAY)){
            orderLogsDescribe = "用户生成订单 支付宝主动回调确认支付";
            logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
            logger = "系统自动";
        }
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_PAID_RABBITMQ)){
            orderLogsDescribe = "用户生成订单 消息队列查询支付状态延迟确认";
            logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
            logger = "系统自动";
        }
        if(Objects.equals(type,StateData.ORDER_STATE_NOPAY_TO_PAID_USER)){
            orderLogsDescribe = "用户申请退单 查询支付状态时发现已支付 但时间上仍可申请退单";
            logType = StateData.ORDER_LOG_PERSON_TYPE_USER;
            UserInfoDto user = basicInfoUtil.getUserInfo(order.getUserInfoId());
            logger = user.getUserInfoName();
        }
        System.out.println("================2 生成订单日志================================");
        rabbitTemplate.convertAndSend("orderLog",orderId+","+orderLogsDescribe+","+logType+","+logger);

        // 生成订单流水表
        System.out.println("================3 添加订单流水================================");
        rabbitTemplate.convertAndSend("moneyByAdd",orderId);
    }

    /**
     * (0,2) 支付订单且支付时间在开始时间之后
     * 涉及接口: 支付宝主动回调接口 消息队列查询支付状态接口 用户主动退单接口
     * 条件: 状态正确 且 支付时间在开始时间之后
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeNopayToShbegin(String orderId, Integer type, LocalDateTime payTime) throws Exception {
        boolean type1 = Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_SHBEGIN_RABBITMQ);

        OrderInfo order = firstCheck(orderId, StateData.ORDER_STATE_NO_PAID);
        order.setOrderInfoState(StateData.ORDER_STATE_CAN_BEGIN);
        order.setOrderPayTime(payTime);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);

        if(type1){
            System.out.println("===================1 修改订单状态========================");
            System.out.println("订单状态:"+order.getOrderInfoState()+"(0待支付 1已支付 2可开始 3已开始 4需结束 5已结束 6已取消)");
            System.out.println("订单修改时间:"+order.getOrderUpdateTime());
            System.out.println("订单支付时间:"+order.getOrderPayTime());
        }

        // 生成订单日志
        String orderLogsDescribe = null;
        Integer logType = null;
        String logger = null;
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_SHBEGIN_ALIPAY)){
            orderLogsDescribe = "用户生成订单 支付宝主动回调确认支付 同时开启服务";
            logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
            logger = "系统自动";
        }
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_SHBEGIN_RABBITMQ)){
            orderLogsDescribe = "用户生成订单 消息队列查询支付状态延迟确认 同时开启服务";
            logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
            logger = "系统自动";
        }
        if(Objects.equals(type,StateData.ORDER_STATE_NOPAY_TO_SHBEGIN_USER)){
            orderLogsDescribe = "用户申请退单 查询支付状态时发现已支付 但已经开启服务不能退单";
            logType = StateData.ORDER_LOG_PERSON_TYPE_USER;
            UserInfoDto user = basicInfoUtil.getUserInfo(order.getUserInfoId());
            logger = user.getUserInfoName();
        }
        System.out.println("================2 生成订单日志================================");
        rabbitTemplate.convertAndSend("orderLog",orderId+","+orderLogsDescribe+","+logType+","+logger);

        // 生成订单流水表
        System.out.println("================3 添加订单流水================================");
        rabbitTemplate.convertAndSend("moneyByAdd",orderId);
    }

    /**
     * (0,6) 未支付状态取消订单 或 固定时间内不支付自动取消订单
     * 涉及接口: 用户主动退单接口 或 消息队列查询支付状态接口
     * 条件: 状态正确 且 根据支付宝返回确实未支付
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeNopayToCancel(String orderId, Integer type) throws Exception {
        boolean type1 = Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_PAID_RABBITMQ);
        if(type1){
            System.out.println("===============1 修改订单状态=======================");
        }
        OrderInfo order = firstCheck(orderId, StateData.ORDER_STATE_NO_PAID);
        order.setOrderInfoState(StateData.ORDER_STATE_CANCELED);
        order.setIsAutoCancel(StateData.ORDER_STATE_CANCEL_BY_SYSTEM);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);
        if(type1){
            System.out.println("订单状态:"+order.getOrderInfoState()+"(0待支付 1已支付 2可开始 3已开始 4需结束 5已结束 6已取消)");
            System.out.println("订单修改时间:"+order.getOrderUpdateTime());
        }

        String orderLogsDescribe = null;
        Integer logType = null;
        String logger = null;
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_PAID_ALIPAY)){
            orderLogsDescribe = "用户未支付状态取消订单 经确认确实未支付";
            logType = StateData.ORDER_LOG_PERSON_TYPE_USER;
            UserInfoDto user = basicInfoUtil.getUserInfo(order.getUserInfoId());
            logger = user.getUserInfoName();
        }
        if(Objects.equals(type, StateData.ORDER_STATE_NOPAY_TO_PAID_RABBITMQ)){
            orderLogsDescribe = "用户生成订单 消息队列查询支付状态延迟确认 未支付取消订单";
            logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
            logger = "系统自动";
        }

        if(type1){
            System.out.println("===============2 生成订单日志=======================");
        }
        rabbitTemplate.convertAndSend("orderLog",orderId+","+orderLogsDescribe+","+logType+","+logger);
    }

    /**
     * (1,2) 扫描到开始时间为现在的(1)订单
     * 涉及接口: 定时任务开始时间扫描接口
     * 条件: 状态正确
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangePaidToShbegin(List<Integer> orderIds){
        LocalDateTime now = LocalDateTime.now();
        System.out.println("=========================批量修改订单状态==========================");
        System.out.println("订单修改时间:"+now);
        System.out.println("订单状态:"+StateData.ORDER_STATE_CAN_BEGIN+"(0待支付 1已支付 2可开始 3已开始 4需结束 5已结束 6已取消)");
        orderInfoDao.update(null,new UpdateWrapper<OrderInfo>().in("order_info_id",orderIds).set("order_info_state",StateData.ORDER_STATE_CAN_BEGIN).set("order_update_time",now));

        StringBuilder orderIdsStr = new StringBuilder();
        for (Integer orderId : orderIds) {
            if(!orderIdsStr.toString().equals("")) orderIdsStr.append(",");
            orderIdsStr.append(orderId);
        }
        String orderLogsDescribe = "订单开始时间到 车辆可进场停车";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
        String logger = "系统自动";
        System.out.println("=========================批量添加订单日志==========================");
        rabbitTemplate.convertAndSend("orderLogBatch",orderIdsStr+","+orderLogsDescribe+","+logType+","+logger);
    }

    /**
     * (1,6) 支付状态取消订单 距离开始时间半小时以上退全款 否则退半款
     * 涉及接口: 用户主动退单接口
     * 条件: 状态正确 或 (0)状态查询支付宝后发现已支付
     *
     * 参数 orderId 订单Id
     *     isHalf   true表示半价退款 false表示全价退款
     * */
    public void stateChangePaidToCancel(String orderId,boolean isHalf) throws Exception {
        OrderInfo order = firstCheck(orderId, StateData.ORDER_STATE_IS_PAID);
        order.setOrderInfoState(StateData.ORDER_STATE_CANCELED);
        order.setIsAutoCancel(StateData.ORDER_STATE_CANCEL_BY_USER);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);

        String orderLogsDescribe = "用户支付状态退款"+(isHalf?"(半额)":"(全额)");
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_USER;
        UserInfoDto user = basicInfoUtil.getUserInfo(order.getUserInfoId());
        String logger = user.getUserInfoName();
        rabbitTemplate.convertAndSend("orderLog",orderId+","+orderLogsDescribe+","+logType+","+logger);

        rabbitTemplate.convertAndSend("moneyByDelete",orderId+","+isHalf);
    }

    /**
     * (2,5) 扫描到结束时间为现在的(2)订单
     * 涉及接口: 定时任务结束时间扫描接口
     * 条件: 状态正确
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeShbeginToEnd(List<Integer> orderIds){
        orderInfoDao.update(null,new UpdateWrapper<OrderInfo>().in("order_info_id",orderIds).set("order_info_state",StateData.ORDER_STATE_END).set("order_update_time",LocalDateTime.now()));

        StringBuilder orderIdsStr = new StringBuilder();
        for (Integer orderId : orderIds) {
            if(!orderIdsStr.toString().equals("")) orderIdsStr.append(",");
            orderIdsStr.append(orderId);
        }
        String orderLogsDescribe = "订单结束时间到 用户全程未使用该服务";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
        String logger = "系统自动";
        rabbitTemplate.convertAndSend("orderLogBatch",orderIdsStr+","+orderLogsDescribe+","+logType+","+logger);
    }

    /**
     * (2,3) 记录到(2)订单入场
     * 涉及接口 车辆进出报告接口
     * 条件: 状态正确
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeShbeginToBegin(String orderId){
        OrderInfo order = firstCheck(orderId, StateData.ORDER_STATE_CAN_BEGIN);
        order.setOrderInfoState(StateData.ORDER_STATE_BEGIN);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);

        String orderLogsDescribe = "车辆到达车位 开始使用服务";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_CERTIFIED;
        String logger = order.getCertifiedInfoName();
        rabbitTemplate.convertAndSend("orderLog",orderId+","+orderLogsDescribe+","+logType+","+logger);

    }

    /**
     * (3,4) 扫描到结束时间为现在的(3)订单 且 最新的入场纪录没有结束时间
     * 涉及接口: 定时任务结束时间扫描接口
     * 条件: 状态正确 且 最新入场记录没有结束时间
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeBeginToShend(List<Integer> orderIds){
        orderInfoDao.update(null,new UpdateWrapper<OrderInfo>().in("order_info_id",orderIds).set("order_info_state",StateData.ORDER_STATE_SHOULD_END).set("order_update_time",LocalDateTime.now()).set("is_overtime",StateData.ORDER_IS_OVERTIME_NOT_FINISH));

        StringBuilder orderIdsStr = new StringBuilder();
        for (Integer orderId : orderIds) {
            if(!orderIdsStr.toString().equals("")) orderIdsStr.append(",");
            orderIdsStr.append(orderId);
        }
        String orderLogsDescribe = "订单结束时间到 但车辆还未离开车位";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
        String logger = "系统自动";
        rabbitTemplate.convertAndSend("orderLogBatch",orderIdsStr+","+orderLogsDescribe+","+logType+","+logger);

        // 新增物业超时订单数
        List<OrderInfo> orders = orderInfoDao.selectList(new QueryWrapper<OrderInfo>().in("order_info_id", orderIds));
        for (OrderInfo order : orders) {
            if(redisUtil.exist(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId()))){
                int orderOvertimeNum = Integer.parseInt(redisUtil.get(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId())));
                redisUtil.set(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId()),String.valueOf(orderOvertimeNum+1));
            }else{
                redisUtil.set(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId()),"1");
            }
        }
    }

    /**
     * (3,5) 扫描到结束时间为现在的(3)订单 且 最新的入场记录有结束时间
     * 涉及接口: 定时任务结束时间扫描接口
     * 条件: 状态正确 且 最新入场记录有结束时间
     *
     * 参数 orderId 订单Id
     * */
    public void stateChangeBeginToEnd(List<Integer> orderIds){
        orderInfoDao.update(null,new UpdateWrapper<OrderInfo>().in("order_info_id",orderIds).set("order_info_state",StateData.ORDER_STATE_END).set("order_update_time",LocalDateTime.now()));

        StringBuilder orderIdsStr = new StringBuilder();
        for (Integer orderId : orderIds) {
            if(!orderIdsStr.toString().equals("")) orderIdsStr.append(",");
            orderIdsStr.append(orderId);
        }
        String orderLogsDescribe = "订单结束时间到 车辆已经离开车位 订单结束";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_SYSTEM;
        String logger = "系统自动";
        rabbitTemplate.convertAndSend("orderLogBatch",orderIdsStr+","+orderLogsDescribe+","+logType+","+logger);
    }

    /**
     * (4,5) 记录到(4)订单出场
     * 涉及接口: 车辆进出报告接口
     * 条件: 状态正确
     *
     * 参数 entranceInfoId 出入场信息
     * */
    public void stateChangeShendToEnd(Integer entranceInfoId){
        EntranceInfo entranceInfo = entranceInfoDao.selectById(entranceInfoId);
        OrderInfo order = orderInfoDao.selectOne(new QueryWrapper<OrderInfo>().eq("stall_info_id", entranceInfo.getStallInfoId()).eq("order_info_state", StateData.ORDER_STATE_SHOULD_END));
        order.setOrderInfoState(StateData.ORDER_STATE_END);
        order.setOrderUpdateTime(LocalDateTime.now());
        orderInfoDao.updateById(order);

        String orderLogsDescribe = "超时车辆已离开车位 自动生成超时订单";
        Integer logType = StateData.ORDER_LOG_PERSON_TYPE_CERTIFIED;
        String logger = order.getCertifiedInfoName();
        rabbitTemplate.convertAndSend("orderLog",order.getOrderInfoNum()+","+orderLogsDescribe+","+logType+","+logger);

        rabbitTemplate.convertAndSend("orderOvertime",entranceInfo.getEntranceInfoId());

        int orderOvertimeNum = Integer.parseInt(redisUtil.get(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId())));
        redisUtil.set(RedisKey.certifiedOrderOvertime(order.getCertifiedInfoId()),String.valueOf(orderOvertimeNum-1));
    }
}
